package xin.marcher.module.common.tair;

import com.aliyun.tair.tairstring.params.CasParams;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;
import xin.marcher.module.common.enums.TairCommand;

/**
 * <p>
 *
 * </p>
 */
@Component
@Slf4j
public class TairLock {

    private TairCache tairCache;

    public TairLock(TairCache tairCache) {
        this.tairCache = tairCache;
    }

    /**
     * 锁过期时间 10s
     */
    private static final int LOCK_EXPIRE_TIME = 10;

    private static final int lockWaitTimeOut = 10000;

    private static final int retryTime = 100;

    /**
     * 加锁
     *
     * @param cacheKey
     * @param randomValue
     * @return
     */
    public boolean tryLock(String cacheKey, String randomValue) {
        if (StringUtils.isEmpty(cacheKey)) {
            return true;
        }
        int waitTimeOut = lockWaitTimeOut;
        try {
            while (waitTimeOut > 0) {
                // 如果put成功说明加锁成功
                if (this.lock(cacheKey, randomValue)) {
                    return true;
                }
                waitTimeOut -= retryTime;
                // 休眠一段时间, 再次尝试加锁
                sleep(retryTime);
            }

            // 程序走到这里说明锁等待一定的时间,所以这里释放这个锁
            unlock(cacheKey, randomValue);
        } catch (Exception ex) {
            log.error("getCacheLock exception key:{}", cacheKey, ex);
        }

        return true;
    }

    public boolean lock(String resourceKey, String randomValue) {
        try (
                Jedis jedis = tairCache.getJedis()) {
            String result = jedis.set(resourceKey, randomValue, new SetParams().nx().ex(LOCK_EXPIRE_TIME));
            return "OK".equals(result);
        }
    }

    /**
     * 解锁
     *
     * @param resourceKey
     * @param randomValue
     * @return
     */
    public boolean unlock(String resourceKey, String randomValue) {
        try (Jedis jedis = tairCache.getJedis()) {
            jedis.getClient().sendCommand(TairCommand.CAD, resourceKey, randomValue);
            Long ret = jedis.getClient().getIntegerReply();
            return 1 == ret;
        }
    }

    /**
     * 续约
     *
     * @param resourceKey
     * @param randomValue
     * @param expireTime
     * @return
     */
    public boolean renewDistributedLock(String resourceKey, String randomValue, int expireTime) {
        try (Jedis jedis = tairCache.getJedis()) {
            jedis.getClient().sendCommand(TairCommand.CAS, resourceKey, randomValue, randomValue, "EX", String.valueOf(expireTime));
            Long ret = jedis.getClient().getIntegerReply();
            return 1 == ret;
        }
    }

    /**
     * 解锁
     *
     * @param lockKey
     * @return
     */
    public boolean unlock(String lockKey) {
        try (Jedis jedis = tairCache.getJedis()) {
            Long result = jedis.del(lockKey);
            return 1 == result;
        } catch (Exception ex) {
            log.error("delete lockKey exception key:{}", lockKey, ex);
        }
        return false;
    }

    private void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (Exception ex) {
            log.error("sleep exception", ex);
        }
    }


    /**
     * 续租
     *
     * @param lockKey
     * @param randomValue
     * @param expireTime
     * @return
     */
    public boolean renewLock(String lockKey, String randomValue, int expireTime) {
        try (Jedis jedis = tairCache.getJedis()) {
            Long result = tairCache.createTairString(jedis).cas(lockKey, randomValue, randomValue, new CasParams().ex(expireTime));
            return 1 == result;
        }
    }

}
